package com.ytjj.qmyx.supplychain.api.annotation.oplog;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ytjj.qmyx.supplychain.api.annotation.oplog.annotation.OperationLog;
import com.ytjj.qmyx.supplychain.common.utils.IPUtil;
import com.ytjj.qmyx.supplychain.common.utils.TokenUtil;
import com.ytjj.qmyx.supplychain.mapper.entity.YxAdmin;
import com.ytjj.qmyx.supplychain.mapper.entity.YxAdminLog;
import com.ytjj.qmyx.supplychain.service.AdminLogService;
import com.ytjj.qmyx.supplychain.service.AdminService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * @Description 全局操作日志拦截器
 *
 * 
 */
@Aspect
@Slf4j
@Component
public class OperationLogAop {
    @Autowired
    private AdminLogService adminLogService;
    @Autowired
    private AdminService adminService;
    @Value("${log.version:1.0}")
    private String operVer;

    @Value("${spring.application.name:}")
    private String applicationName = "";

    /**
     * 日志切入点
     */
    @Pointcut("@annotation(com.ytjj.qmyx.supplychain.api.annotation.oplog.annotation.OperationLog)")
    public void operationLogPointcut() {
    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     */
    @Pointcut("execution(* com.ytjj.qmyx.supplychain.api.controller..*.*(..))")
    public void operExceptionLogPoinCut() {
    }

    /**
     * 方法执行前-> 开始前清除环境变量内容
     *
     * @param joinPoint
     */
    @Before("operationLogPointcut()")
    public void before(JoinPoint joinPoint) {
        log.info("开始执行操作日志记录");
        // 清理本地变量
        OperationLogger.clear();
    }

    /**
     * 方法执行后-> 操作日记处理。
     *
     * @param joinPoint
     */
    @AfterReturning(pointcut = "operationLogPointcut()", returning = "keys")
    public void afterDo(JoinPoint joinPoint, Object keys) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        YxAdminLog adminLog = new YxAdminLog();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取操作
            OperationLog opLog = method.getAnnotation(OperationLog.class);
            if (opLog != null) {
                String operModul = opLog.optModule();
                String operType = opLog.optType();
                String operDesc = opLog.desc();
                // 操作模块
                adminLog.setOperModule(operModul);
                // 操作类型
                adminLog.setOperType(operType);
                // 操作描述
                adminLog.setOperDesc(operDesc);
            }
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            // 请求方法
            adminLog.setOperMethod(methodName);

            // 请求的参数
            Object[] args = joinPoint.getArgs();
            Object[] arguments = new Object[args.length];
            //将参数所在的数组转换成json
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof ServletRequest
                        || args[i] instanceof ServletResponse
                        || args[i] instanceof MultipartFile) {
                    continue;
                }
                arguments[i] = args[i];
            }
            String paramter = "";
            if (arguments != null) {
                try {
                    paramter = JSONObject.toJSONString(arguments);
                } catch (Exception e) {
                    paramter = arguments.toString();
                }
            }
            // 请求参数
            adminLog.setOperRequestParam(paramter);
            // 返回结果
            adminLog.setOperResponseParam(JSON.toJSONString(keys));

            try {
                YxAdmin admin = adminService.getCurAdmin();
                // 请求用户ID
                adminLog.setUserId(admin.getId());
                // 请求用户名称
                adminLog.setUserName(admin.getName());
            } catch (Exception e) {
                log.error("===获取当前用户异常==={}", e);
            }

            try {
                // 请求IP
                adminLog.setOperIp(IPUtil.getIpAddress(TokenUtil.getRequest()));
            } catch (Exception e) {

            }

            // 请求URI
            adminLog.setOperUri(request.getRequestURI());
            // 创建时间
            adminLog.setCreateTime(new Date());
            // 操作版本
            adminLog.setOperVersion(operVer);
            adminLog.setBusinessMsg(OperationLogger.getRemark());
            log.info("===记录产品日志==={}", adminLog);
            adminLogService.insertByBean(adminLog);
        } catch (Exception e) {
            log.error("记录操作日志异常", e);
        } finally {
            // 清理本地变量
            OperationLogger.clear();
        }
    }

    /**
     * 异常返回通知，用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param joinPoint 切入点
     * @param e         异常信息
     */
    @AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

//        AdminErrorLog adminErrorLog = new AdminErrorLog();
        YxAdminLog adminLog = new YxAdminLog();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            // 请求方法名
            adminLog.setOperMethod(methodName);

            // 请求的参数
            Object[] args = joinPoint.getArgs();
            Object[] arguments = new Object[args.length];
            //将参数所在的数组转换成json
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof ServletRequest
                        || args[i] instanceof ServletResponse
                        || args[i] instanceof MultipartFile) {
                    continue;
                }
                arguments[i] = args[i];
            }
            String paramter = "";
            if (arguments != null) {
                try {
                    paramter = JSONObject.toJSONString(arguments);
                } catch (Exception exception) {
                    paramter = arguments.toString();
                }
            }
            // 请求参数
            adminLog.setOperRequestParam(paramter);
            adminLog.setOperIp("");
            // 发生异常时间
            adminLog.setCreateTime(new Date());
            adminLogService.insertByBean(adminLog);
        } catch (Exception e2) {
            e2.printStackTrace();
        }

    }

    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }

}
